//
// Test Suite for geos::operation::valid::IsValidOp class
// Ported from JTS junit/operation/valid/ValidSelfTouchingRingFormingHoleTest.java rev. 1.4

#include <tut/tut.hpp>
// geos
#include <geos/operation/valid/IsValidOp.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/CoordinateArraySequence.h>
#include <geos/geom/Dimension.h>
#include <geos/geom/Geometry.h>
#include <geos/geom/LineString.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/PrecisionModel.h>
#include <geos/util/GEOSException.h>
#include <geos/io/WKTReader.h>
#include <geos/operation/valid/TopologyValidationError.h>
// std
#include <cmath>
#include <string>
#include <memory>
#include <iostream>

using namespace geos::geom;
//using namespace geos::operation;
using namespace geos::operation::valid;
using namespace geos::util;
using namespace std;

namespace tut {
//
// Test Group
//

struct test_ValidSelfTouchingRingFormingHole_data {
    typedef std::unique_ptr<Geometry> GeomPtr;

    geos::geom::PrecisionModel pm_;
    typedef geos::geom::GeometryFactory GeometryFactory;
    geos::geom::GeometryFactory::Ptr factory_;
    geos::io::WKTReader rdr;

    test_ValidSelfTouchingRingFormingHole_data()
        : pm_(1)
        , factory_(GeometryFactory::create(&pm_, 0))
        , rdr(factory_.get())
    {}

    GeomPtr
    fromWKT(const std::string& wkt)
    {
        GeomPtr geom;
        try {
            geom = rdr.read(wkt);
        }
        catch(const GEOSException& ex) {
            cerr << ex.what() << endl;
        }
        return geom;
    }

    void
    checkIsValidDefault(const std::string& wkt, bool expected)
    {
        GeomPtr geom = fromWKT(wkt);
        IsValidOp validator(geom.get());
        bool isValid = validator.isValid();
        ensure(isValid == expected);
    }

    void
    checkIsValidSTR(const std::string& wkt, bool expected)
    {
        GeomPtr geom = fromWKT(wkt);
        IsValidOp validator(geom.get());
        validator.setSelfTouchingRingFormingHoleValid(true);
        bool isValid = validator.isValid();
        ensure(isValid == expected);
    }

};

typedef test_group<test_ValidSelfTouchingRingFormingHole_data> group;
typedef group::object object;

group test_ValidSelfTouchingRingFormingHole_group("geos::operation::valid::ValidSelfTouchingRingFormingHole");

//
// Test Cases
//

// 1 - testShellAndHoleSelfTouch
//
//  Tests a geometry with both a shell self-touch and a hole self=touch.
//  This is valid if STR is allowed, but invalid in OGC
//
template<>
template<>
void object::test<1>
()
{
    string wkt =
        "POLYGON ((0 0, 0 340, 320 340, 320 0, 120 0, 180 100, 60 100, 120 0, 0 0),   (80 300, 80 180, 200 180, 200 240, 280 200, 280 280, 200 240, 200 300, 80 300))";
    checkIsValidSTR(wkt, true);
    checkIsValidDefault(wkt, false);
}

/*
 * 2 - testShellHoleAndHoleHoleTouch
 *
 * Tests a geometry representing the same area as in
 * {@link #testShellAndHoleSelfTouch}
 * but using a shell-hole touch and a hole-hole touch.
 * This is valid in OGC.
 */
template<>
template<>
void object::test<2>
()
{
    string wkt =
        "POLYGON ((0 0, 0 340, 320 340, 320 0, 120 0, 0 0),   (120 0, 180 100, 60 100, 120 0),   (80 300, 80 180, 200 180, 200 240, 200 300, 80 300),  (200 240, 280 200, 280 280, 200 240))";
    checkIsValidSTR(wkt, true);
    checkIsValidDefault(wkt, true);
}

/*
 * 3 - testShellSelfTouchHoleOverlappingHole
 *
 * Tests an overlapping hole condition, where one of the holes is
 * created by a shell self-touch.
 * This is never valid.
 */
template<>
template<>
void object::test<3>
()
{
    string wkt =
        "POLYGON ((0 0, 220 0, 220 200, 120 200, 140 100, 80 100, 120 200, 0 200, 0 0),   (200 80, 20 80, 120 200, 200 80))";
    checkIsValidSTR(wkt, false);
    checkIsValidDefault(wkt, false);
}

/*
 * 4 - testDisconnectedInteriorShellSelfTouchAtNonVertex
 *
 * Ensure that the Disconnected Interior condition is not validated
 */
template<>
template<>
void object::test<4>
()
{
    string wkt = "POLYGON ((40 180, 40 60, 240 60, 240 180, 140 60, 40 180))";
    checkIsValidSTR(wkt, false);
    checkIsValidDefault(wkt, false);
}

/*
 * 5 - testDisconnectedInteriorShellSelfTouchAtVertex
 *
 * Ensure that the Disconnected Interior condition is not validated
 */
template<>
template<>
void object::test<5>
()
{
    string wkt = "POLYGON ((20 20, 20 100, 140 100, 140 180, 260 180, 260 100, 140 100, 140 20, 20 20))";
    checkIsValidSTR(wkt, false);
    checkIsValidDefault(wkt, false);
}

/// 6 - testShellCross()
template<>
template<>
void object::test<6>
()
{
    string wkt = "POLYGON ((20 20, 120 20, 120 220, 240 220, 240 120, 20 120, 20 20))";
    checkIsValidSTR(wkt, false);
    checkIsValidDefault(wkt, false);
}

/// 7 - testShellCrossAndSTR
template<>
template<>
void object::test<7>
()
{
    string wkt = "POLYGON ((20 20, 120 20, 120 220, 180 220, 140 160, 200 160, 180 220, 240 220, 240 120, 20 120,  20 20))";
    checkIsValidSTR(wkt, false);
    checkIsValidDefault(wkt, false);
}



} // namespace tut
